/*
 * 在本控制器中，您将再次看到如何查询内容项，但这次内容项是新创建的Person内容类型。
 * 这与之前的内容类型区别不大，但需要注意ContentItems存储在文档中（包含序列化的部件和字段），
 * 并且可以有多个索引记录引用同一个内容项（例如之前创建的PersonPartIndex会索引PersonPart中的数据）。
 *
 * 还将看到如何通过代码修改和创建内容项的示例。
 *
 * 注意：没有为Person的编辑器显示演示自定义控制器或操作。请进入管理页面（/Admin）并创建几个Person内容项，
 * 包括一些年龄大于30岁的。
 *
 * 主要功能：
 * - 演示内容项的查询、修改和创建
 * - 展示内容项索引的使用
 * - 演示内容项的显示形状构建
 * - 展示内容项的批量操作
 */

using Lombiq.TrainingDemo.Indexes;
using Lombiq.TrainingDemo.Models;
using Microsoft.AspNetCore.Mvc;
using OrchardCore.ContentFields.Fields;
using OrchardCore.ContentManagement;
using OrchardCore.ContentManagement.Display;
using OrchardCore.DisplayManagement.ModelBinding;
using OrchardCore.Modules;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using YesSql;

namespace Lombiq.TrainingDemo.Controllers;

/// <summary>
/// 人员列表控制器 - 演示内容项的查询、修改和创建
/// 
/// 主要用途：
/// - 查询特定条件的内容项
/// - 修改内容项属性
/// - 创建新内容项
/// - 展示内容项的显示形状
/// 
/// 使用场景：
/// - 查询年龄大于30岁的人员
/// - 批量修改内容项属性
/// - 通过代码创建新内容项
/// - 展示内容项的自定义视图
/// 
/// 设计原因：
/// - 演示内容项的高级操作
/// - 展示内容项索引和批量处理
/// - 提供内容项显示和编辑的最佳实践
/// 
/// 设计模式：
/// - 依赖注入模式：通过构造函数注入所需服务
/// - 索引模式：使用索引高效查询内容项
/// - 显示模式：构建内容项显示形状
/// - 批量处理模式：循环处理内容项集合
/// </summary>
public sealed class PersonListController : Controller
{
    private readonly ISession _session;
    private readonly IClock _clock;
    private readonly IContentItemDisplayManager _contentItemDisplayManager;
    private readonly IUpdateModelAccessor _updateModelAccessor;
    private readonly IContentManager _contentManager;

    /// <summary>
    /// 构造函数 - 注入所需的服务依赖
    /// </summary>
    /// <param name="session">YesSql会话，用于数据库操作</param>
    /// <param name="clock">时钟服务，用于获取当前时间</param>
    /// <param name="contentItemDisplayManager">内容项显示管理器</param>
    /// <param name="updateModelAccessor">模型更新访问器</param>
    /// <param name="contentManager">内容管理器</param>
    public PersonListController(
        ISession session,
        IClock clock,
        IContentItemDisplayManager contentItemDisplayManager,
        IUpdateModelAccessor updateModelAccessor,
        IContentManager contentManager)
    {
        _session = session;
        _clock = clock;
        _contentItemDisplayManager = contentItemDisplayManager;
        _updateModelAccessor = updateModelAccessor;
        _contentManager = contentManager;
    }

    /// <summary>
    /// 查询年龄大于30岁的人员
    /// 
    /// 功能说明：
    /// - 查询PersonPartIndex.BirthDateUtc早于30年前的内容项
    /// - 构建内容项的显示形状
    /// - 展示自定义视图
    /// 
    /// 查询过程：
    /// - 使用索引高效过滤内容项
    /// - 构建Summary显示形状
    /// 
    /// 访问方式：
    /// - URL：/Lombiq.TrainingDemo/PersonList/OlderThan30
    /// </summary>
    /// <returns>返回人员显示形状集合的视图</returns>
    public async Task<IActionResult> OlderThan30()
    {
        var thresholdDate = _clock.UtcNow.AddYears(-30);
        var people = await _session
            // 查询PersonPartIndex.BirthDateUtc早于阈值日期的内容项。
            .Query<ContentItem, PersonPartIndex>(index => index.BirthDateUtc < thresholdDate)
            .ListAsync();

        // 构建内容项的显示形状。
        var shapes = await people.AwaitEachAsync(async person =>
        {
            // 通过ISession检索内容项后，需要运行LoadAsync()初始化所有内容。
            await _contentManager.LoadAsync(person);
            return await _contentItemDisplayManager.BuildDisplayAsync(person, _updateModelAccessor.ModelUpdater, "Summary");
        });

        return View(shapes);
    }

    /// <summary>
    /// 批量修改内容项属性（让90岁以上的人变年轻）
    /// 
    /// 功能说明：
    /// - 查询90岁以上的人员内容项
    /// - 修改其出生日期为18年前
    /// - 更新并发布内容项
    /// - 返回修改结果
    /// 
    /// 查询与修改过程：
    /// - 查询PersonPartIndex.BirthDateUtc早于90年前的内容项
    /// - 使用Alter方法安全修改内容部件和字段
    /// - 使用UpdateAsync和PublishAsync保存和发布内容项
    /// 
    /// 访问方式：
    /// - URL：/Lombiq.TrainingDemo/PersonList/FountainOfEternalYouth
    /// </summary>
    /// <returns>返回修改结果字符串</returns>
    public async Task<string> FountainOfEternalYouth()
    {
        // 查询90岁以上的人员内容项。
        var thresholdDate = _clock.UtcNow.AddYears(-90);
        var oldPeople = (await _session
            .Query<ContentItem, PersonPartIndex>(index => index.BirthDateUtc < thresholdDate)
            .ListAsync())
            .ToList();

        foreach (var person in oldPeople)
        {
            // 初始化内容项。
            await _contentManager.LoadAsync(person);
            var eighteenYearOld = _clock.UtcNow.AddYears(-18);

            // 使用Alter方法安全修改内容部件和字段。
            person.Alter<PersonPart>(part =>
            {
                part.BirthDateUtc = eighteenYearOld;
                part.Biography.Text += " I'm young again!";
            });

            // 保存并发布内容项。
            await _contentManager.UpdateAsync(person);
            await _contentManager.PublishAsync(person);
        }

        return "People modified: " +
            (oldPeople.Count != 0 ?
                string.Join(", ", oldPeople.Select(person => person.As<PersonPart>().Name)) :
                "Nobody. Did you create people older than 90?");
    }

    /// <summary>
    /// 通过代码创建新人员内容项
    /// 
    /// 功能说明：
    /// - 创建新的Person内容项
    /// - 设置姓名、出生日期、习惯用手等属性
    /// - 设置内容字段
    /// - 保存并发布内容项
    /// - 返回创建结果
    /// 
    /// 创建过程：
    /// - 使用NewAsync实例化内容项
    /// - 使用Alter方法设置部件属性
    /// - 使用Alter<TField>方法设置字段内容
    /// - 依次调用CreateAsync、UpdateAsync、PublishAsync保存和发布内容项
    /// 
    /// 访问方式：
    /// - URL：/Lombiq.TrainingDemo/PersonList/CreateAnAndroid
    /// </summary>
    /// <returns>返回创建结果字符串</returns>
    public async Task<string> CreateAnAndroid()
    {
        // 创建新内容项。
        var person = await _contentManager.NewAsync(ContentTypes.PersonPage);
        var serialNumber = _clock.UtcNow.Ticks;
        var name = $"X Doe #{serialNumber.ToTechnicalString()}";

        // 设置部件属性。
        person.Alter<PersonPart>(part =>
        {
            part.Name = name;
            part.BirthDateUtc = _clock.UtcNow;
            part.Handedness = Handedness.Right;
        });

        // 设置内容字段。
        var personPart = person.As<PersonPart>();
        personPart.Alter<TextField>(nameof(PersonPart.Biography), field => field.Text = "I'm sentient now!");

        // 保存并发布内容项。
        await _contentManager.CreateAsync(person, VersionOptions.Draft);
        await _contentManager.UpdateAsync(person);
        await _contentManager.PublishAsync(person);

        return $"{name} came into existence.";
    }
}
